Skip to main content

第 6 章:Dockerfile 建置映像檔

Dockerfile 用來建立映像檔的文件

使用者可以撰寫 dockerfile,來讓產生映像檔的流程自動化。撰寫完畢後,使用 docker build 來建立映像檔

Docker-Compose 管理多個容器設定

  • 用來管理多個容器,並定義每個容器間的相依性、參數...等細節

編寫出的檔案為 .yml 格式。撰寫完畢後,可以使用 docker-compose up / down 來輕鬆啟用、刪除多個容器。

Dockerfile 基本結構

Dockerfile 由一行行命令語句組成,並且支援以 # 開頭的註解行。一般而言,Dockerfile 分為四部分:基底映像檔資訊、維護者資訊、映像檔操作指令和容器啟動時執行指令

dockerfile
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..

# 基本映像檔,必須是第一個指令
FROM ubuntu


# 維護者: docker_user <docker_user at email.com> (@docker_user)
MAINTAINER docker_user docker_user@email.com


# 更新映像檔的指令
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf


# 建立新容器時要執行的指令
CMD /usr/sbin/nginx

其中,一開始必須指明作為基底的映像檔名稱,接下來說明維護者資訊(建議)。接著則是映像檔操作指令,例如 RUN 指令,RUN 指令將對映像檔執行相對應的命令。每運行一條 RUN 指令,映像檔就會新增一層。最後是 CMD 指令,指定執行容器時的操作命令。

dockerfile
# Nginx
#
# VERSION 0.0.1

FROM ubuntu
MAINTAINER Victor Vieux <victor@docker.com>

RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server

# Firefox over VNC
#
# VERSION 0.3

FROM ubuntu

# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get update && apt-get install -y x11vnc xvfb firefox
RUN mkdir /.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc'

EXPOSE 5900
CMD ["x11vnc", "-forever", "-usepw", "-create"]

# Multiple images example
#
# VERSION 0.1

FROM ubuntu
RUN echo foo > bar
# Will output something like ===> 907ad6c2736f

FROM ubuntu
RUN echo moo > oink
# Will output something like ===> 695d7793cbe4

# You᾿ll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
# /oink.

Dockerfile 指令

FROM
FROM <image>
FROM <image>:<tag>
  • 第一條指令必須為 FROM 指令。並且,如果在同一個 Dockerfile 中建立多個映像檔時,可以使用多個 FROM 指令(每個映像檔一次)
MAINTAINER
MAINTAINER <name>
  • 指定維護者訊息
RUN
RUN $COMMAND
RUN ["executable", "<param1>", "<param2>"]
  • RUN $COMMAND 在 shell 終端中運行命令,即 /bin/sh -c
  • RUN ["executable", "$PARA1", "$PARA2"] 使用 exec 執行
  • 指定使用其它終端可以透過第二種方式實作,RUN ["/bin/bash", "-c", "echo hello"]
  • 每條 RUN 指令將在當前映像檔基底上執行指定命令,並產生新的映像檔
CMD
CMD ["executable","$PARA1", "$PARA2"] 
  • 使用 exec 執行,推薦使用 CMD $COMMAND $PARA1 $PARA2/bin/sh 中執行,使用在給需要互動的指令;CMD ["$PARA1", "$PARA2"] 提供給 ENTRYPOINT 的預設參數
  • 指定啟動容器時執行的命令,每個 Dockerfile 只能有一條 CMD 命令。如果指定了多條命令,只有最後一條會被執行。如果使用者啟動容器時候指定運行的命令,則會覆蓋掉 CMD 指定的命令
EXPOSE
EXPOSE <port> [<port>...]
  • 設定 Docker 伺服器容器對外的埠號,供外界使用。在啟動容器時需要透過 -P,Docker 會自動分配一個埠號轉發到指定的埠號。
ENV
ENV <key> <value>
  • 指定一個環境變數,會被後續 RUN 指令使用,並在容器運行時保持
ENV PG_MAJOR 9.3ENV PG_VERSION 9.3.4RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
ADD
ADD <src> <dest>
  • 該命令將複製指定的 <src> 到容器中的 <dest>。其中 <src> 可以是 Dockerfile 所在目錄的相對路徑;也可以是一個 URL;還可以是一個 tar 檔案(其複製後會自動解壓縮)。
COPY
COPY <src> <dest>

複製本地端的 <src>(為 Dockerfile 所在目錄的相對路徑)到容器中的 <dest>。當使用本地目錄為根目錄時,推薦使用 COPY

ENTRYPOINT
ENTRYPOINT ["executable", "<param1>", "<param2>"]
ENTRYPOINT <command> <param1> <param2>
  • 指定容器啟動後執行的命令,並且不會被 docker run 提供的參數覆蓋。每個 Dockerfile 中只能有一個 ENTRYPOINT,當指定多個時,只有最後一個會生效。第二條指令要在 shell 中執行。
VOLUME
VOLUME ["/data"]

建立一個可以從本地端或其他容器掛載的掛載點,一般用來存放資料庫和需要保存的資料

USER
USER daemon
  • 指定運行容器時的使用者名稱或 UID,後續的 RUN 也會使用指定使用者。當服務不需要管理員權限時,可以透過該命令指定運行使用者。並且可以在之前建立所需要的使用者,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres。要臨時取得管理員權限可以使用 gosu,而不推薦 sudo
WORKDIR
WORKDIR </path/to/workdir>
  • 為後續的 RUNCMDENTRYPOINT 指令指定工作目錄。可以使用多個 WORKDIR 指令,後續命令如果參數是相對路徑,則會基於之前命令指定的路徑。例如:WORKDIR /aWORKDIR bWORKDIR cRUN pwd則最終路徑為 /a/b/c
ONBUILD
ONBUILD [instruction]
...
ONBUILD ADD . /app/srcONBUILD RUN /usr/local/bin/python-build --dir /app/src
...
FROM image-A
#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
  • 指定當建立的映像檔作為其它新建立映像檔的基底映像檔時,所執行的操作指令。例如,Dockerfile 使用以下的內容建立了映像檔 image-A
  • 如果基於 image-A 建立新的映像檔時,新的 Dockerfile 中使用 FROM image-A指定基底映像檔時,會自動執行 ONBUILD 指令內容,等於在後面新增了兩條指令
  • 使用 ONBUILD 指令的映像檔,推薦在標籤中註明,例如 ruby:1.9-onbuild

建立映像檔

編輯完成 Dockerfile 之後,可以透過 docker build 命令建立映像檔。

基本的格式為 docekr build [選項] 路徑,該命令將讀取指定路徑下(包括子目錄)的 Dockerfile,並將該路徑下所有內容發送給 Docker 伺服端,由伺服端來建立映像檔。因此一般會建議放置 Dockerfile 的目錄為空目錄。也可以透過 .dockerignore 檔案(每一行新增一條排除模式:exclusion patterns)來讓 Docker 忽略路徑下的目錄和檔案。

要指定映像檔的標籤資訊,可以透過 -t 選項

sudo docker build -t myrepo/myapp /tmp/test1/

從映像檔產生 Dockerfile

CenturyLinkLabs 釋出 dockerfile-from-image 工具,以逆向工程建立出 Dockerfile。 類似 docker history 指令,透過映像檔每一層的 metadata 來重建出那 Dockerfile ,即便沒有提供任何資訊。

  • 使用方法

首先 docker pull centurylink/dockerfile-from-image 這已包好 Ruby script 的映像檔, 接下來,執行下面命令,就可得到反推所產生的 Dockerfile.txt :

docker run -v /var/run/docker.sock:/var/run/docker.sock \
centurylink/dockerfile-from-image <IMAGE_TAG_OR_ID> > Dockerfile.txt

<IMAGE_TAG_OR_ID> 參數可以任何包含 tag 的映像檔名稱。

  • 範例

以下是個示範,如何將官方 Ruby 的映像檔來產生出 Dockerfile

dockerfile-from-image
docker pull ruby
Pulling repository ruby

docker run -v /run/docker.sock:/run/docker.sock centurylink/dockerfile-from-image
Usage: dockerfile-from-image.rb [options] <image_id>
-f, --full-tree Generate Dockerfile for all parent layers
-h, --help Show this message

docker run -v /run/docker.sock:/run/docker.sock centurylink/dockerfile-from-image ruby
FROM buildpack-deps:latest
RUN useradd -g users user
RUN apt-get update && apt-get install -y bison procps
RUN apt-get update && apt-get install -y ruby
ADD dir:03090a5fdc5feb8b4f1d6a69214c37b5f6d653f5185cddb6bf7fd71e6ded561c in /usr/src/ruby
WORKDIR /usr/src/ruby
RUN chown -R user:users .
USER user
RUN autoconf && ./configure --disable-install-doc
RUN make -j"$(nproc)"
RUN make check
USER root
RUN apt-get purge -y ruby
RUN make install
RUN echo 'gem: --no-rdoc --no-ri' >> /.gemrc
RUN gem install bundler
ONBUILD ADD . /usr/src/app
ONBUILD WORKDIR /usr/src/app
ONBUILD RUN [ ! -e Gemfile ] || bundle install --system

自動化 Docker 建置流程

在 Dockerfile 裡面,撰寫安裝 Java 和 Tomcat 的指令,包成 Docker Image, 之後只要呼叫到 docker run 指令,就可以把 Tomcat Service 啟動起來,主要目的用來體驗如何撰寫 Dockerfile 和 Build Image 以及執行 Docker container。

建立 Dockerfile

mkdir docker-test
cd docker-test
  • 下載 JDK 的安裝檔放在 docker-test資料夾裡,使用的版本是 jdk-8u152
  • 撰寫 Dockerfile
vi Dockerfile
Dockerfile
FROM centos:7
MAINTAINER jack

RUN yum install -y wget

RUN cd /

ADD jdk-8u152-linux-x64.tar.gz /

RUN wget http://apache.stu.edu.tw/tomcat/tomcat-7/v7.0.82/bin/apache-tomcat-7.0.82.tar.gz
RUN tar zxvf apache-tomcat-7.0.82.tar.gz

ENV JAVA_HOME=/jdk1.8.0_152
ENV PATH=$PATH:/jdk1.8.0_152/bin
CMD ["/apache-tomcat-7.0.82/bin/catalina.sh", "run"]

FROM: 使用到的 Docker Image 名稱,今天使用 CentOS

MAINTAINER: 用來說明,撰寫和維護這個 Dockerfile 的人是誰,也可以給 E-mail的資訊

RUN: RUN 指令後面放 Linux 指令,用來執行安裝和設定這個 Image 需要的東西

ADD: 把 Local 的檔案複製到 Image 裡,如果是 tar.gz 檔複製進去 Image 時會順便自動解壓縮。Dockerfile 另外還有一個複製檔案的指令 COPY 未來還會再介紹

ENV: 用來設定環境變數

CMD: 在指行 docker run 的指令時會直接呼叫開啟 Tomcat Service

Build Docker Image

  1. 預設在和 Dockerfile 檔案同層的資料夾底下輸入, docker build 指令
docker build -t mytomcat .--no-cache

使用 --no-cache 的主要原因,是避免在 Build Docker image 時被 cache 住,而造成沒有 build 到修改過的 Dockerfile。

  1. Build 完 Docker Image 之後,使用 docker images 指令查看是否有 build 成功

執行 Docker Container

docker run mytomcat

Tomcat Service 會被執行起來的主要原因是在 Dockerfile 裡面有寫 CMD 指令,呼叫啟動 Tomcat Service

打開 Browser 確認

  • 要打開 Browser 確認 Tomcat Service 有沒有被執行起來時,發現我們不知道 Docker Container 的 IP,這時侯只能使用 docker exec 進入 docker container查詢 IP 。
  • 要使用 docker exec 指令之前需要先知道 Container 的 ID 所以需要先使用 docker ps 指令查詢 Container ID
  • 有了 IP 之後就可以打開 Browser 輸入 http://172.17.0.2:8080 URL的位置,確認 Tomcat Service 是否有啟動
docker run -p 8080:8080 mytomcat